#include<stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdint.h>
#include "myalloc.h"
#include "algoritmos_memoria.h"
#include <mcheck.h>
#include<time.h>
/*
 filename:firstfit.c
 description:implementaciòn de particiones dinamicas
 Author:Martin Zavatto
 */

struct memory*start;

void* cache; //puntero al bloque de memoria donde voy a guardar las claves y los valores





void store(char clave[50] , void *valor, struct memory *current){

		int32_t clavesize = strlen(clave)+1;
		int32_t valorsize = strlen(valor)+1;
		int32_t ps = valorsize;
		struct memory* prev = NULL;
		struct memory* block = (struct memory*) myalloc(sizeof(struct memory));
		//si el dato es menor que el tamaño minimo de bloque, le asigno el tamaño minimo.
		if(ps<minb)
			ps=minb;
		block->bsz = ps;
		block->data = current->data;
		strcpy(block->key,clave);
		block->keysz = clavesize;
		block->datasz = valorsize;
		block->stored = 1;
		block->lastAccsess = time(NULL);
		//si ocupe todo el bloque libre
		if(current->bsz - ps > 0){
			block->link = current;
			//el actual ahora es el siguiente
			current->bsz = current->bsz - ps;
			current->data = block->data + block->bsz;
		}
		else{
			block->link = current->link;
		}
		if (current == start)/*si entra en el primer bloque*/
		{
			start = block;
		} else {
			prev = find_prev(current);
			prev->link = block;

		}
		memcpy(block->data,valor,valorsize);
}


struct memory* find_prev(struct memory* current){
	struct  memory* aux = start;
	struct memory* prev = NULL;
	while(aux != NULL){
		if(aux == current){
			break;
		}
		prev = aux;
		aux = aux->link;
	}
	return prev;
}

void updateBLockTime(struct memory* block, int32_t criterio){
	if(criterio == FIFO){

	}
	else{
		block->lastAccsess = time(NULL);
	}
}

struct memory* best_fit(int32_t dataSize) {
    struct memory* bestBlock = NULL;
    struct memory* current = start;
    int32_t min=0;
    int32_t aux=0;
    int32_t flag=0;

    while (current != NULL) {
        if (current->stored == 0) {
            aux = current->bsz - dataSize;
            if(aux >=0 && flag == 0){
            	min = aux;
            	flag=1;
            }

            if(aux >=0 && aux <= min){
                min = aux;
                bestBlock = current;
            }
        }
        current = current->link;
    }
    if (bestBlock == NULL){
        printf("No se enconcontro espacio disponible, usando Best fit\n");
    }else{
        printf("Se enconcontro espacio de %d en la posicion %p, usando Best fit\n", dataSize, bestBlock);
    }
    return bestBlock;
}


struct memory* first_fit(int32_t ps) {
	 struct memory* current = start;
	/*busco el primer bloque de memoria suficientemente grande*/
	while (current != NULL) {
		if (current->stored == 0 && current->bsz >= ps) {
			break;
		}
		current = current->link;
	}
	if (current == NULL)
		printf("No hay lugar capo\n");

	return current;
}

void compactar(){
	struct memory* current = start;
		int32_t bsz_aux;
		int32_t compactado = 0;

		while(current != NULL && current->link != NULL){
			compactado=0;
			if(current->stored == 0 && current->link->stored==1){
				//me guardo el size del bloque libre
				bsz_aux = current->bsz;
				//muevo el los datos del bloque actual al bloque libre anterior
				memcpy(current->data,current->link->data,current->link->datasz);
				current->datasz = current->link->datasz;
				current->keysz = current->link->keysz;
				strcpy(current->key,current->link->key);
				current->bsz = current->link->bsz;
				//lo marco como ocupado
				current->stored = 1;
				//el bloque actual ahora tiene el tamaño que tenia el libre
				current->link->bsz = bsz_aux;
				current->link->data= current->data + current->datasz;
				current->link->stored = 0;
				//vuelvo al principio
				compactado = 1;
				current = start;
			}
			//si hay dos libres consecutivos los junto
			if(current->stored == 0 && current->link->stored==0){
				current->bsz = current->bsz + current->link->bsz;
				current->link = current->link->link;
				//myfree(current->link);
				//vuelvo al principio
				compactado = 1;
				current = start;
			}
			//me muevo al siguiente bloque
			if(compactado == 0)
				current = current->link;
		}

}

void dealocate_memory_clave(char* clave) {
	struct memory* current = start;
	//struct memory* prev = NULL;
	while (current != NULL) {
		int32_t esIgual =strcmp(current->key,clave);
		if(esIgual == 0)
			break;
		else
			current = current->link;
	}
	if (current == NULL) {
		printf("No se encontro la clave\n");
	} else {
		//si el siguiente esta libre los junto en uno
		/**if (current->link != NULL && current->link->stored == 0) {
			current->bsz = current->bsz + current->link->bsz;
			prev = current->link;
			current->link = prev->link;
			prev->link = NULL;
			myfree(prev);

		}*/
		current->stored = 0;

	}
}


void dealocate_item(int32_t criterio, struct memory *start) {
	struct memory* block = NULL;
	switch (criterio){
	case FIFO:
		block = get_fifo(start);
		break;
	case LRU:
		block = get_lru(start);
		break;
	}
	if(block == NULL)
		printf("Memoria libre, no hay items para borrar");
	else
		//lo marco como libre
		block->stored = 0;
}

struct memory* get_fifo(struct memory *start){
	struct memory *current = start;
	struct memory *fifoBlock = NULL;
	int32_t flag = 0;

	while (current != NULL) {
			if(current->stored!=0 && flag==0){
				flag = 1;
				fifoBlock = current;
			}
			if(current->stored!=0 && current->lastAccsess < fifoBlock->lastAccsess)
				fifoBlock = current;
			current = current->link;
	}
	return fifoBlock;
}

struct memory* get_lru(struct memory *start){
	struct memory *current = start;
	struct memory *lruBlock = NULL;
	int32_t flag = 0;
	while (current != NULL) {
			if(current->stored!=0 && flag==0){
				flag = 1;
				lruBlock = current;
			}
			if(current->stored!=0 && current->lastAccsess > lruBlock->lastAccsess)
				lruBlock = current;
			current = current->link;
	}
	return lruBlock;
}

void * get_value(char* clave,int32_t criterio){
	struct memory* current = start;
	while (current != NULL) {
		if(current->stored!=0){
			int32_t esIgual =strcmp(current->key,clave);
			if(esIgual == 0)
				break;
		}
		else
			current = current->link;
	}
	if (current == NULL) {
		printf("No se encontro la clave\n");
	} else {
		updateBLockTime(current,criterio);
		return current->data;
	}
	return NULL;

}
void show_mem_status() {
	int32_t c = 1, free = 0, aloc = 0;
	struct memory* current = start;
	printf("Memory Status is::\n");
	printf("---Block---BSZ---BS---BE---Valor---Clave---Flag---keysz---datasz--lastModify\n");
	while (current != NULL) {
		char* data = (char*) current->data;
		char *clave= (char*) current->key;
		printf("%d %d %p %p %s %s %d %d %d %ld\n", c++, current->bsz, current->data, current->data+current->bsz-1, data,clave,
				current->stored, current->keysz, current->datasz, current->lastAccsess);

		if (current->stored == 0) {
			free = free + current->bsz;
		} else {
			aloc = aloc + current->bsz;
		}
		current = current->link;
	}
	printf("Memoria libre= %d\n", free);
	printf("Memoria alocada= %d\n", aloc);

}

int32_t main() {
	mtrace();
    //segundos desde 1970
	time_t seconds;
	seconds = time (NULL);
	printf ("%ld milliseconds since January 1, 1970\n", seconds);
	printf ("%d Tamanio de time \n", sizeof(time_t));
	int32_t ch;
	char cc;
	//uso estos malloc solo como prueba. Estos datos me van a venir desde la llamada de la funcion
	char clave[50];
	char valor[5];
	/*	Solicito la memoria que voy a utilizar en mi proceso
	 *  maxmsz: max tamaño de bloque para datos PREGUNTAR CUANTO ES CONVENIENTE!!
	 *  cantinad de bloques necesarios si tomo como tamaño minimo de bloque 4 bytes (esto puede cambiar) PREGUNTAR
	 *
	 */
	int32_t tamanio_memoria= maxmsz + ((sizeof(struct memory))* (maxmsz/minb));
    InitMem(tamanio_memoria,minb);
    //los primeros 4Kb son para mi data
    cache = myalloc(maxmsz);
    printf("%s %p\n", "Posicion de memoria" , cache);
	start = (struct memory*) myalloc(sizeof(struct memory));
	//INICIALIZO MI BLOQUE DE MEMORIA ES UN GRAN BLOQUE DE 4K
	start->bsz = maxmsz;
	start->data = cache;
	start->keysz = -1;
	start->stored = 0;
	start->link = NULL;
	start->lastAccsess = time(NULL);
	/*......................................................................*/

	while (1) {
		printf("\t1. Guardar Clave - Valor\n");
		printf("\t2. Obtener valor\n");
		printf("\t3. Mostrar estado de la memoria\n");
		printf("\t4. Quitar Dato de memoria\n");
		printf("\t5. Compactar\n");
		printf("\t6. Salir\n");
		printf("Ingresar opcion: ");
		scanf("%d", &ch);
		switch (ch) {
		case 1:
			do {
				printf("Ingrese una clave: ");
				scanf("%s",clave);
				printf("Ingrese un valor: ");
				scanf("%s",valor);
				int32_t valorsize = strlen(valor)+1;
				int32_t ps = valorsize;
				struct memory* bloque = NULL;
				bloque = best_fit(ps);
				if(bloque == NULL)
					printf("No se encontro lugar suficiente\n");
				else
					store(clave,valor,bloque);
				show_mem_status();
				printf(
						"Ingresar nueva Clave - Valor(y/n)?: ");
				scanf("%c", &cc);
				scanf("%c", &cc);

			} while (cc == 'y');
			break;
		case 3:
			show_mem_status();
			break;
		case 4:
				dealocate_item(LRU,start);
				show_mem_status();
				break;
		case 2:
				printf("Ingresar Clave: ");
				scanf("%s", clave);
				char* data =(char*) get_value(clave,LRU);
				if(data != NULL)
					printf("%s\n",data);
				break;
		case 5:
			compactar();
			show_mem_status();
			break;
		case 6:
			break;
		default:
			printf("\nIngrese un valor valido!!\n");
		}
		if (ch == 6)
			break;
	}
	return 0;
}


